# -*- coding: utf-8 -*-
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
#作者：cacho_37967865
#博客：https://blog.csdn.net/sinat_37967865
#文件：tensorflow_mnist_quick.py
#日期：2019-11-12
#备注：没有使用dropout 优化手段进行、GradientDescentOptimizer优化器、
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
import time
import tensorflow as tf
from tensorflow.examples.tutorials.mnist import input_data

sTime = time.time()
mnist = input_data.read_data_sets('F:\PythonProject\Mnist', one_hot=True)

input = tf.placeholder(tf.float32,[None,784])           # None　代表图片数量未知
y = tf.placeholder(tf.float32,[None,10])                # y是最终预测的结果


# 下面４个方法都是工具方法，为了帮助我们创造神经网络的．
def conv2d(input,filter):
    return tf.nn.conv2d(input,filter,strides=[1,1,1,1],padding='SAME')              # 创造卷积层的方法：input 代表输入，filter 代表卷积核

def max_pool(input):
    return tf.nn.max_pool(input,ksize=[1,2,2,1],strides=[1,2,2,1],padding='SAME')   # 池化层

def weight_variable(shape):
    initial = tf.truncated_normal(shape,stddev=0.1)      # 初始化卷积核或者是权重数组的值
    return tf.Variable(initial)

def bias_variable(shape):
    return tf.Variable(tf.zeros(shape))                  # 初始化偏置量（bias）


# 第一层卷积：定义了卷积核
#filter = [3,3,1,32]     # 97.6%
filter = [5,5,1,32]      # 98.2%
filter_conv1 = weight_variable(filter)
b_conv1 = bias_variable([32])
input_image = tf.reshape(input,[-1,28,28,1])                       # 将input 重新调整结构，适用于CNN的特征提取
h_conv1 = tf.nn.relu(conv2d(input_image,filter_conv1)+b_conv1)     # 创建卷积层，进行卷积操作，并通过Relu激活，然后池化
h_pool1 = max_pool(h_conv1)


# 密集连接层：定义了卷积层的结构．
W_fc1 = weight_variable([14*14*32,784])       # 一层卷积 28/2=14
b_fc1 = bias_variable([784])
h_flat = tf.reshape(h_pool1,[-1,14*14*32])      # 将pool后的卷积核全部拉平成一行数据，便于和后面的全连接层进行数据运算
h_fc1 = tf.matmul(h_flat,W_fc1) + b_fc1


# 输出层：softmax层
W_fc2 = weight_variable([784,10])
b_fc2 = bias_variable([10])
y_hat = tf.matmul(h_fc1,W_fc2) + b_fc2         # 是整个神经网络的输出层，包含10个结点

# 代价函数采用了cross_entropy，显然整个模型输出的值经过了softmax处理，将输出的值换算成每个类别的概率．
cross_entropy = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(labels=y,logits=y_hat))

# 训练神经网络和测试神经网络了
# （train_step在每一次训练后都会调整神经网络中参数的值，以便cross_entropy这个代价函数的值最低，也就是为了神经网络的表现越来越好）
train_step = tf.train.GradientDescentOptimizer(0.01).minimize(cross_entropy)    # 定义了一个梯度下降的训练器，学习率是0.01

# 定义准确率
correct_prediction = tf.equal(tf.argmax(y_hat,1),tf.argmax(y,1))
accuracy = tf.reduce_mean(tf.cast(correct_prediction,tf.float32))

saver = tf.train.Saver()

with tf.Session() as sess:
    sess.run(tf.global_variables_initializer())
    for i in range(10000):                            # 训练10000个周期
        batch_x,batch_y = mnist.train.next_batch(50)  # 每个周期训练都是小批量训练50张
        train_step.run(feed_dict={input: batch_x, y: batch_y})
        if i % 100 == 0:
            train_accuracy = accuracy.eval(feed_dict={input: batch_x,y: batch_y})
            print("step %d,train accuracy %g " %(i,train_accuracy))

    saver.save(sess, 'F:\PythonProject\Mnist\model\quick\mnist.ckpt')  # 保存模型参数，注意把这里改为自己的路径
    print("test accuracy %g " % accuracy.eval(feed_dict={input: mnist.test.images,y: mnist.test.labels}))

eTime = time.time()
s = eTime - sTime
print('花费的时间为：%.2f秒' % (s))